
// StringCompression.cpp

// Implements the wrapping functions for compression and decompression using AString as their data

#include "Globals.h"
#include "StringCompression.h"
#include "zlib.h"





/// Compresses a_Data into a_Compressed; return Z_XXX error constants same as zlib's compress2()
int CompressString(const char * a_Data, int a_Length, AString & a_Compressed)
{
	uLongf CompressedSize = compressBound(a_Length);
	
	// HACK: We're assuming that AString returns its internal buffer in its data() call and we're overwriting that buffer!
	// It saves us one allocation and one memcpy of the entire compressed data
	// It may not work on some STL implementations! (Confirmed working on MSVC 2008 & 2010)
	a_Compressed.resize(CompressedSize);
	int errorcode = compress2( (Bytef*)a_Compressed.data(), &CompressedSize, (const Bytef*)a_Data, a_Length, Z_DEFAULT_COMPRESSION);
	if (errorcode != Z_OK)
	{
		return errorcode;
	}
	a_Compressed.resize(CompressedSize);
	return Z_OK;
}





/// Uncompresses a_Data into a_Decompressed; returns Z_XXX error constants same as zlib's uncompress()
int UncompressString(const char * a_Data, int a_Length, AString & a_Uncompressed, int a_UncompressedSize)
{
	// HACK: We're assuming that AString returns its internal buffer in its data() call and we're overwriting that buffer!
	// It saves us one allocation and one memcpy of the entire compressed data
	// It may not work on some STL implementations! (Confirmed working on MSVC 2008 & 2010)
	int ExpUncompressed = a_UncompressedSize;
	a_Uncompressed.resize(a_UncompressedSize);
	
	// _X: This is the code copied from zlib's uncompress() function, with some logging added:
	// This code will be removed in favor of the zlib's uncompress() function call, as soon as we sort out the -5 error
	// http://forum.mc-server.org/showthread.php?tid=412
	// http://forum.mc-server.org/showthread.php?tid=416
	z_stream stream;
	int err;

	stream.next_in = (Bytef*)a_Data;
	stream.avail_in = (uInt)((uLongf)a_Length);
	/* Check for source > 64K on 16-bit machine: */
	if ((uLong)stream.avail_in != (uLong)a_Length)
	{
		LOGERROR("stream.avail_in truncated from %d to %u", a_Length, stream.avail_in);
		return Z_BUF_ERROR;
	}

	stream.next_out = (Bytef*)a_Uncompressed.data();
	stream.avail_out = (uInt)((uLongf)a_UncompressedSize);
	if ((uLong)stream.avail_out != (uLong)a_UncompressedSize)
	{
		LOGERROR("stream.avail_out truncated from %d to %u", a_UncompressedSize, stream.avail_out);
		return Z_BUF_ERROR;
	}

	stream.zalloc = (alloc_func)0;
	stream.zfree = (free_func)0;

	err = inflateInit(&stream);
	if (err != Z_OK)
	{
		LOGERROR("inflateInit() failed: %d", err);
		return err;
	}

	err = inflate(&stream, Z_FINISH);
	if (err != Z_STREAM_END)
	{
		LOGERROR("inflate() returned %d, avail_in = %d, avail_out = %d, total_in = %d, total_out = %d", 
			err, 
			stream.avail_in, stream.avail_out,
			stream.total_in, stream.total_out
		);
		inflateEnd(&stream);
		if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
		{
			LOGERROR("returning Z_DATA_ERROR");
			return Z_DATA_ERROR;
		}
		return err;
	}
	a_UncompressedSize = stream.total_out;

	err = inflateEnd(&stream);
	if (err != Z_OK)
	{
		LOGERROR("inflateEnd() returned %d", err);
		return err;
	}

	/*
	// The original code using uncompress() call
	int errorcode = uncompress((Bytef*)a_Uncompressed.data(), (uLongf *)&a_UncompressedSize, (const Bytef*)a_Data, a_Length);
	if (errorcode != Z_OK)
	{
		LOGD("uncompress() failed: err %d, compressed_len %d, uncompressed: exp %d, got %d",
			errorcode, a_Length, ExpUncompressed, a_UncompressedSize
		);
		return errorcode;
	}
	*/
	
	a_Uncompressed.resize(a_UncompressedSize);
	return Z_OK;
}




